package cn.cdu.medical.service.impl;

import cn.cdu.medical.dto.AssistantRequest;
import cn.cdu.medical.dto.ConsultationRequest;
import cn.cdu.medical.entity.Consultation;
import cn.cdu.medical.entity.MedicalRecord;
import cn.cdu.medical.repository.ConsultationMapper;
import cn.cdu.medical.service.ChatMessageService;
import cn.cdu.medical.service.ConsultationService;
import cn.cdu.medical.util.AIResponseParser;
import cn.cdu.basics.utils.SecurityUtil;
import cn.cdu.medical.entity.ChatMessage;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatCompletionResult;
import com.theokanning.openai.service.OpenAiService;
import com.theokanning.openai.client.OpenAiApi;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@Transactional
public class ConsultationServiceImpl extends ServiceImpl<ConsultationMapper, Consultation> implements ConsultationService {

    private static final int MAX_RETRIES = 3;
    private static final long RETRY_DELAY_MS = 1000;

    @Value("${deepseek.api.key}")
    private String apiKey;

    @Value("${deepseek.api.url}")
    private String apiUrl;

    private OpenAiService openAiService;

    @Autowired
    private AIResponseParser aiResponseParser;

    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private ChatMessageService chatMessageService;

    @PostConstruct
    public void initOpenAiService() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message ->
                log.debug("DeepSeek API: {}", message));
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(loggingInterceptor)
                .addInterceptor(chain -> {
                    Request original = chain.request();
                    Request request = original.newBuilder()
                            .header("Authorization", "Bearer " + apiKey)
                            .header("Content-Type", "application/json")
                            .method(original.method(), original.body())
                            .build();
                    return chain.proceed(request);
                })
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(apiUrl)
                .client(client)
                .addConverterFactory(JacksonConverterFactory.create(mapper))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

        OpenAiApi api = retrofit.create(OpenAiApi.class);
        openAiService = new OpenAiService(api);
    }

    private String getAIResponse(String prompt, String systemPrompt, Object context) throws Exception {
        List<com.theokanning.openai.completion.chat.ChatMessage> messages = new ArrayList<>();
        messages.add(new com.theokanning.openai.completion.chat.ChatMessage("system", systemPrompt));

        if (context != null) {
            messages.add(new com.theokanning.openai.completion.chat.ChatMessage("system", "Context: " + context.toString()));
        }
        
        messages.add(new com.theokanning.openai.completion.chat.ChatMessage("user", prompt));

        ChatCompletionRequest chatRequest = ChatCompletionRequest.builder()
                .model("deepseek-chat")
                .messages(messages)
                .temperature(0.7)
                .maxTokens(2000)
                .build();

        return retryOnFailure(() -> {
            ChatCompletionResult result = openAiService.createChatCompletion(chatRequest);
            if (result.getChoices() == null || result.getChoices().isEmpty()) {
                throw new RuntimeException("Empty response from AI service");
            }
            return result.getChoices().get(0).getMessage().getContent();
        });
    }

    private String getCurrentUserId() {
        try {
            return securityUtil.getCurrUser().getId();
        } catch (Exception e) {
            log.warn("Failed to get current user ID: {}", e.getMessage());
            return null;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Consultation saveConsultation(String userId, String symptoms, String response, String type, String orderId) {
        log.debug("Starting saveConsultation transaction for user: {}", userId);
        
        // 1. 保存问诊记录
        Consultation consultation = new Consultation();
        consultation.setUserId(userId != null ? userId : getCurrentUserId());
        consultation.setSymptoms(symptoms);
        consultation.setResponse(response);
        consultation.setType(type);
        consultation.setConsultTime(LocalDateTime.now());
        consultation.setOrderId(orderId);
        
        boolean saved = save(consultation);
        if (!saved) {
            log.error("Failed to save consultation record");
            throw new RuntimeException("Failed to save consultation record");
        }
        
        log.debug("Saved consultation record with ID: {}", consultation.getId());
        
        return consultation;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Consultation chat(ConsultationRequest request) {
        try {
            log.debug("Processing chat request: {}", request);
            String symptoms = request.getMessage();
            String userId = request.getUserId() != null ? request.getUserId() : getCurrentUserId();
            String orderId = request.getOrderId();
            
            if (userId == null) {
                log.warn("No user ID available for chat request");
            }

            String systemPrompt = "你是一个专业的医疗咨询助手。请按以下格式回复：\n" +
                    "【主诉】\n患者的主要症状和不适\n\n" +
                    "【现病史】\n详细描述症状发展过程\n\n" +
                    "【初步诊断】\n根据症状给出初步判断\n\n" +
                    "【建议科室】\n建议就诊的科室\n\n" +
                    "【治疗建议】\n给出初步建议和注意事项";

            log.debug("Sending request to DeepSeek API - System prompt: {}, User message: {}", systemPrompt, symptoms);

            String response = getAIResponse(symptoms, systemPrompt, null);
            
            log.debug("Received response from DeepSeek API: {}", response);

            // 保存咨询记录和电子病历
            Consultation consultation = saveConsultation(userId, symptoms, response, "CHAT", orderId);
            
            // 保存用户消息和AI回复
            ChatMessage userMessage = new ChatMessage();
            userMessage.setConsultationId(consultation.getId());
            userMessage.setContent(symptoms);
            userMessage.setType("user");
            userMessage.setCreateTime(LocalDateTime.now());
            chatMessageService.saveMessage(userMessage);

            ChatMessage aiMessage = new ChatMessage();
            aiMessage.setConsultationId(consultation.getId());
            aiMessage.setContent(response);
            aiMessage.setType("ai");
            aiMessage.setCreateTime(LocalDateTime.now());
            chatMessageService.saveMessage(aiMessage);

            return consultation;

        } catch (Exception e) {
            log.error("Error during chat: ", e);
            throw new RuntimeException("Failed to get response from AI service: " + e.getMessage());
        }
    }

    @Override
    public String checkSymptoms(String symptoms) {
        try {
            log.debug("Processing symptom check request for symptoms: {}", symptoms);

            String systemPrompt = "你是一个专业的医疗诊断助手。请仔细分析患者的症状，并按以下格式回复：\n" +
                    "【症状概述】\n总结患者描述的主要症状和不适\n\n" +
                    "【症状分析】\n详细分析每个症状的可能原因和严重程度\n\n" +
                    "【可能疾病】\n按照可能性从高到低列出可能的疾病，并说明理由\n\n" +
                    "【建议检查】\n建议进行的检查项目，并说明目的\n\n" +
                    "【就医建议】\n就医的紧急程度、建议就诊科室和注意事项\n\n" +
                    "【生活建议】\n在就医前的生活注意事项和自我管理建议";

            String response = getAIResponse(symptoms, systemPrompt, null);
            
            // 保存咨询记录和电子病历
            Consultation consultation = new Consultation();
            consultation.setUserId(getCurrentUserId());
            consultation.setSymptoms(symptoms);
            consultation.setResponse(response);
            consultation.setType("CHECK");
            consultation.setConsultTime(LocalDateTime.now());
            save(consultation);

            log.debug("Symptom check completed successfully");
            return response;

        } catch (Exception e) {
            log.error("Error during symptom check: {}", e.getMessage(), e);
            throw new RuntimeException("症状自查失败：" + e.getMessage());
        }
    }

    @Override
    public MedicalRecord generateInitialMedicalRecord(String consultationId) {
        try {
            log.debug("Generating initial medical record for consultation: {}", consultationId);

            Consultation consultation = getById(consultationId);
            if (consultation == null) {
                throw new RuntimeException("Consultation not found: " + consultationId);
            }

            MedicalRecord record = aiResponseParser.createMedicalRecord(consultation.getResponse());
            record.setConsultationId(consultationId);
            record.setUserId(consultation.getUserId());
            record.setVisitTime(consultation.getConsultTime());

            log.debug("Generated initial medical record: {}", record);
            return record;

        } catch (Exception e) {
            log.error("Error generating initial medical record: ", e);
            throw new RuntimeException("Failed to generate initial medical record: " + e.getMessage());
        }
    }

    @Override
    public String checkContraindications(AssistantRequest request) {
        try {
            log.debug("Processing contraindications check request: {}", request);
            long startTime = System.currentTimeMillis();

            String systemPrompt = "你是一个专业的医疗AI助手。请分析以下用药信息，检查可能的禁忌症。\n" +
                    "回复格式要求：\n" +
                    "1. 使用Markdown格式\n" +
                    "2. 序号使用数字加点，如：1. 2. 3.\n" +
                    "3. 重要提示使用【建议】...【/建议】标记\n" +
                    "4. 按以下结构组织内容：\n" +
                    "   - 药物相互作用\n" +
                    "   - 禁忌症分析\n" +
                    "   - 注意事项\n" +
                    "   - 用药建议";

            String response = getAIResponse(request.getMessage(), systemPrompt, request.getContext());
            
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000; // 转换为秒
            log.info("Contraindications check completed in {} seconds", duration);
            
            return response;
        } catch (Exception e) {
            log.error("Error in contraindications check: {}", e.getMessage(), e);
            throw new RuntimeException("禁忌症检查失败: " + e.getMessage());
        }
    }

    @Override
    public String getMedicineGuide(AssistantRequest request) {
        try {
            log.debug("Processing medicine guide request: {}", request);
            long startTime = System.currentTimeMillis();

            String systemPrompt = "你是一个专业的医疗AI助手。请提供以下药品的使用指南。\n" +
                    "回复格式要求：\n" +
                    "1. 使用Markdown格式\n" +
                    "2. 序号使用数字加点，如：1. 2. 3.\n" +
                    "3. 重要提示使用【建议】...【/建议】标记\n" +
                    "4. 按以下结构组织内容：\n" +
                    "   - 药品信息\n" +
                    "   - 适应症\n" +
                    "   - 用法用量\n" +
                    "   - 禁忌症\n" +
                    "   - 注意事项\n" +
                    "   - 不良反应";

            String response = getAIResponse(request.getMessage(), systemPrompt, request.getContext());
            
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000; // 转换为秒
            log.info("Medicine guide completed in {} seconds", duration);
            
            return response;
        } catch (Exception e) {
            log.error("Error in medicine guide: {}", e.getMessage(), e);
            throw new RuntimeException("药品指南获取失败: " + e.getMessage());
        }
    }

    @Override
    public String getClinicalGuide(AssistantRequest request) {
        try {
            log.debug("Processing clinical guide request: {}", request);
            long startTime = System.currentTimeMillis();

            String systemPrompt = "你是一个专业的医疗AI助手。请提供以下疾病的临床诊疗指南。\n" +
                    "回复格式要求：\n" +
                    "1. 使用Markdown格式\n" +
                    "2. 序号使用数字加点，如：1. 2. 3.\n" +
                    "3. 重要提示使用【建议】...【/建议】标记\n" +
                    "4. 按以下结构组织内容：\n" +
                    "   - 疾病概述\n" +
                    "   - 诊断标准\n" +
                    "   - 治疗方案\n" +
                    "   - 用药建议\n" +
                    "   - 注意事项\n" +
                    "   - 预后评估";

            String response = getAIResponse(request.getMessage(), systemPrompt, request.getContext());
            
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000; // 转换为秒
            log.info("Clinical guide completed in {} seconds", duration);
            
            return response;
        } catch (Exception e) {
            log.error("Error in clinical guide: {}", e.getMessage(), e);
            throw new RuntimeException("临床指南获取失败: " + e.getMessage());
        }
    }

    @Override
    public String analyzeCase(AssistantRequest request) {
        try {
            log.debug("Processing case analysis request: {}", request);
            long startTime = System.currentTimeMillis();

            String systemPrompt = "你是一个专业的医疗AI助手。请分析以下病例信息，并提供专业的建议。\n" +
                    "回复格式要求：\n" +
                    "1. 使用Markdown格式\n" +
                    "2. 序号使用数字加点，如：1. 2. 3.\n" +
                    "3. 分析结果使用【建议】...【/建议】标记\n" +
                    "4. 按以下结构组织内容：\n" +
                    "   - 症状分析\n" +
                    "   - 可能诊断\n" +
                    "   - 建议检查\n" +
                    "   - 治疗方案\n" +
                    "   - 注意事项";

            String response = getAIResponse(request.getMessage(), systemPrompt, request.getContext());
            
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000; // 转换为秒
            log.info("Case analysis completed in {} seconds", duration);
            
            return response;
        } catch (Exception e) {
            log.error("Error in case analysis: {}", e.getMessage(), e);
            throw new RuntimeException("病例分析失败: " + e.getMessage());
        }
    }

    @Override
    public String assistantChat(AssistantRequest request) {
        try {
            log.debug("Processing assistant chat request: {}", request);
            long startTime = System.currentTimeMillis();

            String systemPrompt = "你是一个专业的医疗AI助手。请根据提供的上下文信息，为医生提供专业的建议。\n" +
                    "回复要求：\n" +
                    "1. 使用Markdown格式\n" +
                    "2. 序号使用数字加点，如：1. 2. 3.\n" +
                    "3. 建议使用【建议】...【/建议】标记\n" +
                    "4. 保持专业性和严谨性\n" +
                    "5. 分点列出要点，使用标题区分不同部分";

            String response = getAIResponse(request.getMessage(), systemPrompt, request.getContext());
            
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000; // 转换为秒
            log.info("AI response completed in {} seconds", duration);
            
            return response;
        } catch (Exception e) {
            log.error("Error in assistant chat: {}", e.getMessage(), e);
            throw new RuntimeException("AI助手请求失败: " + e.getMessage());
        }
    }

    private <T> T retryOnFailure(RetryableOperation<T> operation) throws Exception {
        Exception lastException = null;
        for (int i = 0; i < MAX_RETRIES; i++) {
            try {
                return operation.execute();
            } catch (Exception e) {
                lastException = e;
                log.warn("Attempt {} failed: {}", i + 1, e.getMessage());
                if (i < MAX_RETRIES - 1) {
                    Thread.sleep(RETRY_DELAY_MS);
                }
            }
        }
        throw lastException;
    }

    @FunctionalInterface
    private interface RetryableOperation<T> {
        T execute() throws Exception;
    }
} 